最後來看Native 端(iOS):
補充:iOS 中的 .h 和.m 檔
.h 為標頭檔,做為宣告屬性及方法使用
.m 為Objective-C檔,做為實際編寫屬性值及方法內容使用
這邊先介紹一下,Flutter 專案的iOS 入口點在AppDelegate
,其繼承自Flutter.framework 的FlutterAppDelegate
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
在啟動時進入AppDelegate
後會執行GeneratedPluginRegistrant.register(with: self)
,而GeneratedPluginRegistrant
中的 (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry
會實現當執行flutter pub get
時生成的Flutter 所依賴iOS 端的插件
舉例來說,我們來看之前建立plugin 時自動產生的example
(使用我們plugin package 的範例專案),此範例專案已經依賴我們的plugin package,我們就可以看到它的GeneratedPluginRegistrant.m
:
#import "GeneratedPluginRegistrant.h"
#if __has_include(<batterylevel/BatterylevelPlugin.h>)
#import <batterylevel/BatterylevelPlugin.h>
#else
@import batterylevel;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[BatterylevelPlugin registerWithRegistrar:[registry registrarForPlugin:@"BatterylevelPlugin"]];
}
@end
使範例專案在啟動iOS 時,會註冊Flutter 依賴的iOS 端插件(此時會呼叫我們的pluginBatterylevelPlugin
來註冊)
回到我們的plugin,我們來看看如何實現Native 端(iOS)的功能:
預設建立的檔案主要有:
ios/Classes/BatterylevelPlugin
#import "BatterylevelPlugin.h"
#if __has_include(<batterylevel/batterylevel-Swift.h>)
#import <batterylevel/batterylevel-Swift.h>
#else
// Support project import fallback if the generated compatibility header
// is not copied when this plugin is created as a library.
// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
#import "batterylevel-Swift.h"
#endif
@implementation BatterylevelPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftBatterylevelPlugin registerWithRegistrar:registrar];
}
@end
當別人使用我們的plugin 時,在啟動iOS 後會註冊它們Flutter 所依賴的iOS 端插件,我們的plugin 就透過BatterylevelPlugin
提供一個SwiftBatterylevelPlugin
來註冊,其plugin 的功能也會在SwiftBatterylevelPlugin
來實現
ios/Classes/SwiftBatterylevelPlugin.swift
用來實現註冊方法以及我們插件的功能,其中FlutterPlugin
為插件協議,所以必須實作註冊方法
public class SwiftBatterylevelPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "batterylevel", binaryMessenger: registrar.messenger())
let instance = SwiftBatterylevelPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}
在註冊方法中需要新建用來通信的Channel
(對應Flutter 端的MethodChannel
,iOS 端為FlutterMethodChannel
)並與FlutterPluginRegistrar
的FlutterBinaryMessenger
進行連結綁定,再透過調用addMethodCallDelegate
方法將Channel
註冊到FlutterEngine
裡,這邊我們來看看FlutterEngine 裡的原始碼:
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
channel:(FlutterMethodChannel*)channel {
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[delegate handleMethodCall:call result:result];
}];
}
這邊將Channel
透過setMessageHandler
向 FlutterBinaryMessenger
註冊一個FlutterMethodChannel
用的FlutterMethodCallHandler
來處理接收訊息,當Flutter 端通過invokeMethod
調用Native iOS 端方法時,就會通過傳進來的FlutterPlugin 的delegate
將訊息回調給FlutterPlugin
(這邊的SwiftBatterylevelPlugin
)的handleMethodCall
方法
回到SwiftBatterylevelPlugin
,最後handle
用來實現FlutterPlugin
的- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result
,這邊產生的範例程式直接回傳版本資訊給Flutter 端
而範例程式並無針對Flutter 端呼叫FlutterMethodCall
的不同方法名做判斷處理,我們來對其做以下修改:
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
default:
result(FlutterMethodNotImplemented)
}
}
在看完plugin 範例程式的版本資訊功能後,我們對plugin 對Channel
功能的實現已經有初步的了解,這邊就先以一樣的步驟,新增取得batterylevel 電池電量資訊的功能吧